/*****************************************************************************
 * hal_stm32l4_i2c.h
 *
 * Copyright (C) 2020 Jeasonvor <1101627719@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/
#include "plum_api.h"
#include "hal/source/hal_stm32l4/hal_stm32l4.h"
/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/

/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define HAL_STM32_I2C_SOFT_SDA_LOW(I2Cx) \
    plum_hal_gpio_write(I2Cx->gpio.sda, PLUM_HAL_GPIO_LOW)

#define HAL_STM32_I2C_SOFT_SDA_HIGH(I2Cx) \
    plum_hal_gpio_write(I2Cx->gpio.sda, PLUM_HAL_GPIO_HIGH)

#define HAL_STM32_I2C_SOFT_SDA_READ(I2Cx) plum_hal_gpio_read(I2Cx->gpio.sda)

#define HAL_STM32_I2C_SOFT_SDA_OUT(I2Cx)             \
    do {                                             \
        plum_hal_gpio_cof_t gpio;                    \
        gpio.mode       = PLUM_HAL_GPIO_MODE_COMMON; \
        gpio.config.com = PLUM_HAL_GPIO_MODE_OUT_OD; \
        plum_hal_gpio_init(I2Cx->gpio.sda, &gpio);   \
    } while (0)

#define HAL_STM32_I2C_SOFT_SDA_IN(I2Cx)              \
    do {                                             \
        plum_hal_gpio_cof_t gpio;                    \
        gpio.mode       = PLUM_HAL_GPIO_MODE_COMMON; \
        gpio.config.com = PLUM_HAL_GPIO_MODE_INPUT;  \
        plum_hal_gpio_init(I2Cx->gpio.sda, &gpio);   \
    } while (0)

#define HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx) \
    plum_hal_gpio_write(I2Cx->gpio.scl, PLUM_HAL_GPIO_LOW)

#define HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx) \
    plum_hal_gpio_write(I2Cx->gpio.scl, PLUM_HAL_GPIO_HIGH)

/*****************************************************************************
 * Private Types
 ****************************************************************************/

typedef struct {
    /* plum id */
    plum_u32 id;

    plum_hal_i2c_mode_t mode;

    plum_hal_i2c_gpio_t gpio;

    I2C_HandleTypeDef hi2c;

    struct list_head list;
} hal_stm32l1_hdl_t;

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/

/* I2C interface header*/
PLUM_PRIVATE
hal_stm32l1_hdl_t i2c_head;

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

/**
 * @brief:  I2C句柄查询
 *
 * @param[in]:  id  -   plum id
 *
 * @return: 返回I2C操作句柄
 */
PLUM_PRIVATE
hal_stm32l1_hdl_t *hal_stm32_i2c_instance_select(plum_u32 id)
{
    hal_stm32l1_hdl_t *I2Cx = plum_null;

    struct list_head *pos;
    list_for_each(pos, &i2c_head.list)
    {
        I2Cx = list_entry(pos, hal_stm32l1_hdl_t, list);
        if (I2Cx->id == id) {
            break;
        }
    }

    return (I2Cx);
}

/**
 * @brief:  I2C端口注册
 *
 * @param[in]:  id  -   plum id
 *
 * @return: 返回I2C操作句柄
 */
PLUM_PRIVATE
hal_stm32l1_hdl_t *hal_stm32_i2c_instance_register(plum_u32 id)
{
    hal_stm32l1_hdl_t *I2Cx = plum_kal_mem_malloc(sizeof(hal_stm32l1_hdl_t));
    if (!I2Cx) {
        return (NULL);
    }

    I2Cx->id = id;

    switch (PLUM_HAL_I2C_IDX(id)) {
#if defined(I2C1)
        case 1:
            I2Cx->hi2c.Instance = I2C1;
            break;
#endif

#if defined(I2C2)
        case 2:
            I2Cx->hi2c.Instance = I2C2;
            break;
#endif

#if defined(I2C3)
        case 3:
            I2Cx->hi2c.Instance = I2C3;
            break;
#endif

        case 101:
        case 102:
            break;

        default:
            return (NULL);
    }

    list_add(&I2Cx->list, &i2c_head.list);

    return (I2Cx);
}

/**
 * @brief:  软件模拟I2C初始化
 *
 * @param[in]:  id  -   plum id
 * @param[in]:  cof -   I2C配置指针
 *
 * @return: 返回执行状态
 **/
PLUM_PRIVATE
plum_s32 hal_stm32_i2c_soft_init(plum_u32 id, plum_hal_i2c_cof_t *cof)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        hal_stm32l1_hdl_t *I2Cx = hal_stm32_i2c_instance_register(id);
        if (!I2Cx) {
            rc = PLUM_ECODE_EIO;
            break;
        }

        plum_hal_gpio_cof_t gpio;
        gpio.mode       = PLUM_HAL_GPIO_MODE_COMMON;
        gpio.config.com = PLUM_HAL_GPIO_MODE_OUT_PP;
        rc              = plum_hal_gpio_init(cof->scl_pin, &gpio);
        if (rc) {
            break;
        }
        I2Cx->gpio.scl = cof->gpio.scl;

        gpio.config.com = PLUM_HAL_GPIO_MODE_OUT_OD;
        rc              = plum_hal_gpio_init(cof->sda_pin, &gpio);
        if (rc) {
            break;
        }
        I2Cx->gpio.sda = cof->gpio.sda;

        I2Cx->mode = cof->mode;
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_void hal_stm32_i2c_soft_start(hal_stm32l1_hdl_t *I2Cx)
{
    HAL_STM32_I2C_SOFT_SDA_OUT(I2Cx);
    HAL_STM32_I2C_SOFT_SDA_HIGH(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx);

    HAL_STM32_I2C_SOFT_SDA_LOW(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
}

PLUM_PRIVATE
plum_void hal_stm32_i2c_soft_stop(hal_stm32l1_hdl_t *I2Cx)
{
    HAL_STM32_I2C_SOFT_SDA_OUT(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
    HAL_STM32_I2C_SOFT_SDA_LOW(I2Cx);

    HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx);
    HAL_STM32_I2C_SOFT_SDA_HIGH(I2Cx);
}

PLUM_PRIVATE
plum_void hal_stm32_i2c_soft_ack(hal_stm32l1_hdl_t *I2Cx)
{
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
    HAL_STM32_I2C_SOFT_SDA_OUT(I2Cx);
    HAL_STM32_I2C_SOFT_SDA_LOW(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
}

PLUM_PRIVATE
plum_void hal_stm32_i2c_soft_nack(hal_stm32l1_hdl_t *I2Cx)
{
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
    HAL_STM32_I2C_SOFT_SDA_OUT(I2Cx);
    HAL_STM32_I2C_SOFT_SDA_HIGH(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
}

PLUM_PRIVATE
plum_bit hal_stm32_i2c_soft_wait_ack(hal_stm32l1_hdl_t *I2Cx)
{
    HAL_STM32_I2C_SOFT_SDA_IN(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx);
    if (HAL_STM32_I2C_SOFT_SDA_READ(I2Cx)) {
        hal_stm32_i2c_soft_stop(I2Cx);
        return (plum_false);
    }
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
    return (plum_true);
}

PLUM_PRIVATE
plum_void hal_stm32_i2c_soft_send_byte(hal_stm32l1_hdl_t *I2Cx, plum_u8 data)
{
    HAL_STM32_I2C_SOFT_SDA_OUT(I2Cx);
    HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
    plum_u8 loop;
    for (loop = 0; loop < 8; loop++) {
        if ((data >> (7 - loop)) & 0x01) {
            HAL_STM32_I2C_SOFT_SDA_HIGH(I2Cx);
        }
        else {
            HAL_STM32_I2C_SOFT_SDA_LOW(I2Cx);
        }
        HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx);
        HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
    }
}

PLUM_PRIVATE
plum_u8 hal_stm32_i2c_soft_recv_byte(hal_stm32l1_hdl_t *I2Cx, plum_bit ack)
{
    plum_u8 data = 0;
    HAL_STM32_I2C_SOFT_SDA_IN(I2Cx);
    plum_u8 loop;
    for (loop = 0; loop < 8; loop++) {
        HAL_STM32_I2C_SOFT_SCL_LOW(I2Cx);
        HAL_STM32_I2C_SOFT_SCL_HIGH(I2Cx);
        data |= HAL_STM32_I2C_SOFT_SDA_READ(I2Cx) << (7 - loop);
    }
    if (ack) {
        hal_stm32_i2c_soft_ack(I2Cx);
    }
    else {
        hal_stm32_i2c_soft_nack(I2Cx);
    }

    return (data);
}

/*
 * @brief:  硬件I2C初始化
 *
 * @param[in]:  id  -   plum id
 * @param[in]:  cof -   I2C配置指针
 *
 * @return: 返回执行状态
 **/
PLUM_PRIVATE
plum_s32 hal_stm32_i2c_hard_init(plum_u32 id, plum_hal_i2c_cof_t *cof)
{
    plum_s32 rc = PLUM_ECODE_OK;

    return (rc);
}

/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32 plum_hal_i2c_init(plum_u32 id, plum_hal_i2c_cof_t *cof)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_I2C(id)) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if (!cof) {
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if (PLUM_HAL_I2C_IDX(id) < PLUM_HAL_IDX_SIMULAT_BASE) {
            rc = hal_stm32_i2c_hard_init(id, cof);
        }
        else {
            rc = hal_stm32_i2c_soft_init(id, cof);
        }
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_hal_i2c_transmit(plum_u32 id, plum_u8 *wData, plum_u32 len)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_I2C(id)) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if (!wData) {
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if (PLUM_HAL_I2C_IDX(id) < PLUM_HAL_IDX_SIMULAT_BASE) {
        }
        else {
        }
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_hal_i2c_receive(plum_u32 id, plum_u8 *rData, plum_u32 len)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_I2C(id)) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if (!rData) {
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if (PLUM_HAL_I2C_IDX(id) < PLUM_HAL_IDX_SIMULAT_BASE) {
        }
        else {
        }
    } while (0);

    return (rc);
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
